package com.springsource.a02applicationcontext;

import lombok.Data;
import org.slf4j.Logger;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
 * @Author LionSweet
 * @Description //常见的ApplicationContext实现
 * @Date 10:55 2024/12/19
 * @Param
 * @return
 **/
public class TestApplicationContext {
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(TestApplicationContext.class);

    public static void main(String[] args) {
//        实现1
//        testClassPathXmlApplicationContext();
//        实现2
//        testFileSystemXmlApplicationContext();
//        实现1和2体现了ApplicationContext比BeanFactory多出来的ResourcePatternResolver的功能


//        实现3
//        testAnnotationConfigApplicationContext();


//        实现4
        testAnnotationConfigServletWebServerApplicationContext();
    }

    // 较为经典的容器，基于classpath下xml格式的配置文件来创建
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            log.debug("beanDefinitionName: {}", beanDefinitionName);
        }
        Bean2 bean2 = context.getBean(Bean2.class);
        log.info("bean2: {}", bean2);


        // 原理
//        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//        reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
//        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
//            log.debug("beanDefinitionName: {}", beanDefinitionName);
//        }

    }

    // 基于磁盘路径下xml格式的配置文件来创建
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\Codes\\Mine\\spring-source\\src\\main\\resources\\b01.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            log.debug("beanDefinitionName: {}", beanDefinitionName);
        }
        Bean2 bean2 = context.getBean(Bean2.class);
        log.info("bean2: {}", bean2);

        // 这个原理和classpath相同，只不过用的是load的时候加载的是 new FileSystemResource
    }

    // 较为经典的容器，基于java配置类来创建
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            log.debug("beanDefinitionName: {}", beanDefinitionName);
        }
        Bean2 bean2 = context.getBean(Bean2.class);
        log.info("bean2: {}", bean2);

        /**
        相比于实现1和实现2，BeanDefinition中多了Config类，同时多了处理器，相当于在xml里配置了<context:annotation-config/>
         11:33:13.135 [main] DEBUG com.springsource.A02Application - beanDefinitionName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
         11:33:13.136 [main] DEBUG com.springsource.A02Application - beanDefinitionName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
         11:33:13.136 [main] DEBUG com.springsource.A02Application - beanDefinitionName: org.springframework.context.annotation.internalCommonAnnotationProcessor
         11:33:13.136 [main] DEBUG com.springsource.A02Application - beanDefinitionName: org.springframework.context.event.internalEventListenerProcessor
         11:33:13.136 [main] DEBUG com.springsource.A02Application - beanDefinitionName: org.springframework.context.event.internalEventListenerFactory
         **/
    }

    // 较为经典的容器，基于java配置类来创建，用于web环境
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    }

    @Configuration
    static class WebConfig {
        // web容器
        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        // 创建dispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        // 将web容器与dispatcherServlet进行关联
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
//            两个参数：指定哪个dispatcherServlet，指定dispatcherServlet对哪些访问路径进行分发
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        // 通过实现controller接口的方式创建controller
        // 如果Bean的名字以/开头，则可以被解析成访问路径
        @Bean("/hello1")
        public Controller controller1() {
            return new Controller() {
                @Override
                public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
                    log.info("controller1");
                    return null;
                }
            };
        }

        @Bean("/hello2")
        public Controller controller2() {
            return (request, response) -> {
                response.getWriter().print("hello2");
                return null;
            };
        }
    }


    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }



    static class Bean1 {
        public Bean1() {
            log.info("Bean1 init");
        }
    }

    @Data
    static class Bean2 {

        private Bean1 bean1;

        public Bean2() {
            log.info("Bean2 init");
        }
    }

}
